package com.gxitsky.shardingjdbc.common.config;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Range;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.RangeShardingValue;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author gxing
 * @desc 分表算法, 根据userId分片算法 hash(userId) % 4
 * PreciseShardingAlgorithm(精准分片), RangeShardingAlgorithm(范围分片)
 * @date 2020/9/28
 */
@Component
public class UserIdShardingAlgorithm implements PreciseShardingAlgorithm<Long>, RangeShardingAlgorithm<Long> {
    private static final Logger logger = LogManager.getLogger(UserIdShardingAlgorithm.class);

    private static final int TABLES = 4;
    private static List<String> SUFFIX_LIST = new ArrayList<>();

    static {
        for (int i = 0; i < TABLES; i++) {
            SUFFIX_LIST.add(String.valueOf(i));
        }
    }

    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> preciseShardingValue) {
        logger.info("----->availableTargetNames:{}", JSON.toJSONString(availableTargetNames));
        logger.info("----->preciseShardingValue:{}", JSON.toJSONString(preciseShardingValue));

        String logicTableName = preciseShardingValue.getLogicTableName();
        Long userId = preciseShardingValue.getValue();
        String index = hashCodeIndex(userId);
        return logicTableName + "_" + index;
    }

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> rangeShardingValue) {
        logger.info("----->availableTargetNames:{}", JSON.toJSONString(availableTargetNames));
        logger.info("----->rangeShardingValue:{}", JSON.toJSONString(rangeShardingValue));

        String logicTableName = rangeShardingValue.getLogicTableName();
        Range<Long> range = rangeShardingValue.getValueRange();
        logger.info("----->range lowerEndpoint:{}, upperEndpoint:{}", range.hasLowerBound() ? range.lowerEndpoint() : null,
                range.hasUpperBound() ? range.upperEndpoint() : null);

        if (range.hasLowerBound() && range.hasUpperBound()) {
            List<String> indexList = new ArrayList<>();
            for (Long userId = range.lowerEndpoint(); userId <= range.upperEndpoint(); userId++) {
                String index = this.hashCodeIndex(userId);
                indexList.add(index);
            }
            return indexList.stream().map(index -> logicTableName + "_" + index).collect(Collectors.toList());
        }
        return SUFFIX_LIST.stream().map(index -> logicTableName + "_" + index).collect(Collectors.toList());
    }

    /**
     * @desc: 哈希取模
     * @param: [userId]
     * @author: gxing
     * @date: 2020/9/28
     */
    private String hashCodeIndex(Long userId) {
        int index = (userId.hashCode()) % TABLES;
        return String.valueOf(index);
    }
}
